﻿/*
   Copyright 2012 University of Southampton

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

using System;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.Collections.Generic;
using System.Linq;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
using System.Web.UI.HtmlControls;
using uk.ac.soton.ses.DAL;
using System.Web;
using uk.ac.soton.ses.WebControls;

namespace uk.ac.soton.ses.layouts
{
    public partial class DelimitedFileViewer : LayoutsPageBase
    {
        private List<int> requestedFieldNos = null;
        private bool IsInitialised = false;
        private ExperimentDataFile dataFile = null;
        private int maxrows = -1;
        private int startrow = 0;
        private int headerrow = 0;
        private bool useHeaderRow = true;
        private char delim;
        private FileParser_DelimitedFile delimitedFile = null;
        private MaterialsMetadataContext dbcontext = null;

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            this.Page.AddHDCStylesheet();

            this.List_Fields.SelectedIndexChanged += new EventHandler(List_Fields_SelectedIndexChanged);
            this.txt_Rows.TextChanged += new EventHandler(txt_Rows_TextChanged);
            this.txt_StartRow.TextChanged += new EventHandler(txt_StartRow_TextChanged);
            this.txt_HeaderRow.TextChanged += new EventHandler(txt_HeaderRow_TextChanged);
            this.chk_UseHeaderRow.CheckedChanged += new EventHandler(chk_UseHeaderRow_CheckedChanged);
            this.txt_Delimeter.TextChanged += new EventHandler(txt_Delimeter_TextChanged);
            this.btn_SaveReport.Click += new EventHandler(btn_SaveReport_Click);
            this.btn_ExportReport.Click += new EventHandler(btn_ExportReport_Click);
            this.btn_ImportSelectedData.Click += new EventHandler(btn_ImportSelectedData_Click);
            this.btn_GraphFile.Click += new EventHandler(btn_GraphFile_Click);

            this.dbcontext = HttpContext.Current.GetMaterialsDBContext();

            //Remember, HttpValueCollection is case insensitive
            string dataFileIDString = Page.Request.QueryString["DataFileID"];
            string[] requestedFieldNosStrings = Page.Request.QueryString.GetValues("FieldNo") ?? new string[0];
            string[] requestedFields = Page.Request.QueryString.GetValues("Field") ?? new string[0];
            string maxRowsString = Page.Request.QueryString["MaxRows"] ?? "0";
            string startRowsString = Page.Request.QueryString["StartRow"] ?? "0";
            string headerRowString = Page.Request.QueryString["HeaderRow"] ?? "0";
            string useHeaderRowString = Page.Request.QueryString["UseHeaderRow"] ?? "";
            string delimStr = Page.Request.QueryString["Delim"] ?? "\t";

            int dataFileID;

            if (int.TryParse(dataFileIDString, out dataFileID) 
                && int.TryParse(maxRowsString, out this.maxrows) 
                && delimStr.Length >= 1 
                && int.TryParse(startRowsString, out this.startrow)
                && int.TryParse(headerRowString, out this.headerrow))
            {
                this.delim = delimStr[0];
                dataFile = dbcontext.GetExperimentDataFileByID(dataFileID);

                this.useHeaderRow = (string.IsNullOrEmpty(useHeaderRowString) || useHeaderRowString.Equals("true", StringComparison.OrdinalIgnoreCase));

                if (this.maxrows <= 0)
                {
                    this.maxrows = 10;
                }
                this.txt_Rows.Text = maxrows.ToString();

                if (this.headerrow < 1)
                {
                    this.headerrow = 1;
                }

                if (this.useHeaderRow && this.startrow < this.headerrow + 1)
                {
                    this.startrow = this.headerrow + 1;
                }
                else if (!this.useHeaderRow && this.startrow < 1)
                {
                    this.startrow = 1;
                }

                this.txt_StartRow.Text = startrow.ToString();
                this.txt_HeaderRow.Text = headerrow.ToString();

                //Load fields
                if (this.useHeaderRow)
                {
                    this.delimitedFile = new FileParser_DelimitedFile(dataFile.GetFullPath(), this.delim, startrow, headerrow);
                }
                else
                {
                    this.delimitedFile = new FileParser_DelimitedFile(dataFile.GetFullPath(), this.delim, startrow);
                }
                
                //Identitfy which fields were requested by name
                requestedFieldNos = this.GetRequestedFields(requestedFieldNosStrings, requestedFields);

                this.IsInitialised = true;
            }
        }


        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            if (this.IsInitialised && !Page.IsPostBack)
            {
                this.CreateFieldSelectionList();
                this.CreateTable();

                this.lbl_LineCount.Text = this.delimitedFile.LineCount.ToString();
                this.lbl_MaxRows.Text = this.maxrows.ToString();
                this.lbl_StartRow.Text = this.startrow.ToString();
                this.chk_UseHeaderRow.Checked = this.useHeaderRow;
                this.txt_Delimeter.Text = System.Text.RegularExpressions.Regex.Escape(this.delim.ToString());
            }
        }

        /// <summary>
        /// Dispose overridden to make sure that the delimited file object is disposed of correctly
        /// </summary>
        public override void Dispose()
        {
            base.Dispose();
            if (this.delimitedFile != null)
            {
                this.delimitedFile.Dispose();
            }
        }


        private void btn_SaveReport_Click(object sender, EventArgs e)
        {
            string reportUrl = this.Page.Request.Url.AbsoluteUri;

            if (!this.dataFile.ExperimentReference.IsLoaded)
            {
                this.dataFile.ExperimentReference.Load();
            }

            Experiment exp = this.dataFile.Experiment;
            if (exp != null)
            {
                //Add our new report parameter
                ExperimentParameter savedReportParm = exp.SaveParameterValue(this.dbcontext, "Saved report " + DateTime.Now, reportUrl,true);

                //Save the new report parameter
                //dbcontext.SaveChanges();

                //Look up the reports parent parameter
                ExperimentParameter reportsParam = exp.LookupParameter("Saved reports");

                if (reportsParam == null)
                {
                    reportsParam = exp.SaveParameterValue(this.dbcontext, "Saved reports", "", true);
                }

                //Move the report parameter under the reports parent parameter
                reportsParam.AddChildParameter(this.dbcontext, savedReportParm);

                //Save the new parameter link
                dbcontext.SaveChanges();

                //Report what we did to the user
                HDCSharedWebFunctions.AddMessageLabel("Report saved to experiment " + exp.ID, this.ReportButtons);
            }
        }

        //Taken from example at http://msdn.microsoft.com/en-us/library/system.web.httpresponse.outputstream.aspx
        private void btn_ExportReport_Click(object sender, EventArgs e)
        {
            Response.Clear();
            Response.ClearHeaders();
            Response.AddHeader("content-disposition", "attachment; filename=export.txt");
            Response.ContentType = "text/plain";
            Response.BufferOutput = true;

            FileParser_DelimitedFile df;
            if (this.useHeaderRow)
            {
                df = new FileParser_DelimitedFile(dataFile.GetFullPath(), this.delim, startrow, headerrow);
            }
            else
            {
                df = new FileParser_DelimitedFile(dataFile.GetFullPath(), this.delim, startrow);
            }
            try
            {
                using (StreamWriter tw = new StreamWriter(Response.OutputStream))
                {
                    //Print headers only if the first row is a header
                    if (this.useHeaderRow)
                    {
                        List<string> headerfields = new List<string>();
                        for (int i = 0; i < this.delimitedFile.FieldNames.Count; i++)
                        {
                            if (requestedFieldNos.Count() == 0 || requestedFieldNos.Contains(i))
                            {
                                headerfields.Add(this.delimitedFile.FieldNames[i]);
                            }
                        }
                        tw.WriteLine(string.Join(this.delim.ToString(), headerfields.ToArray()));
                    }

                    //Print the rest of the contents
                    Dictionary<int, string> fieldValues;
                    while ((fieldValues = df.GetNextRowValues()) != null)
                    {
                        if (maxrows > 0 && (df.CurrentLineNoRel > maxrows))
                        {
                            //Break if we're done reading
                            break;
                        }

                        List<string> rowfields = new List<string>();
                        foreach (int fieldKey in this.delimitedFile.FieldNames.Keys)
                        {
                            //If there are no specifically requested fields print the data, otherwise check the field was requested
                            if (requestedFieldNos.Count() == 0 || requestedFieldNos.Contains(fieldKey))
                            {
                                //Print the data, only if it is valid, otherwise skip the line
                                if (fieldValues.Count == this.delimitedFile.FieldCount && fieldValues.ContainsKey(fieldKey))
                                {
                                    rowfields.Add(fieldValues[fieldKey]);
                                }
                            }
                        }
                        tw.WriteLine(string.Join(this.delim.ToString(), rowfields.ToArray()));
                    }
                }
                Response.Flush();
                Response.End();
            }
            finally
            {
                df.Dispose();
            }
        }

        private void btn_ImportSelectedData_Click(object sender, EventArgs e)
        {
            if (this.useHeaderRow)
            {
                this.dataFile.ImportAsDelimitedFile(this.dbcontext, true, this.delim, this.startrow, this.headerrow, this.maxrows, this.requestedFieldNos);
            }
            else
            {
                this.dataFile.ImportAsDelimitedFile(this.dbcontext, true, this.delim, this.startrow, -1, this.maxrows, this.requestedFieldNos);
            }

            HDCSharedWebFunctions.AddMessageLabel("Metadata saved to experiment " + this.dataFile.Experiment.ID, this.ReportButtons);
        }

        private void btn_GraphFile_Click(object sender, EventArgs e)
        {
            string url;
            if (this.useHeaderRow)
            {
                url = HDCConfiguration.GetUrl_FileGraphXY(this.dataFile.ID, this.delim, this.startrow, this.headerrow, this.maxrows);
            }
            else
            {
                url = HDCConfiguration.GetUrl_FileGraphXY(this.dataFile.ID, this.delim, this.startrow, -1, this.maxrows);
            }

            Response.Redirect(url);
        }

        private void txt_Delimeter_TextChanged(object sender, EventArgs e)
        {
            string delimStr = this.txt_Delimeter.Text;
            this.delim = HDCSharedFunctions.GetDelimiterFromTextBoxString(delimStr, '\t');
            this.RedirectToNewUrl();
        }

        private void chk_UseHeaderRow_CheckedChanged(object sender, EventArgs e)
        {
            this.useHeaderRow = this.chk_UseHeaderRow.Checked;
            this.RedirectToNewUrl();
        }


        private void txt_Rows_TextChanged(object sender, EventArgs e)
        {
            this.ProcessLimitRows();
        }

        private void txt_StartRow_TextChanged(object sender, EventArgs e)
        {
            this.ProcessLimitRows();
        }

        private void txt_HeaderRow_TextChanged(object sender, EventArgs e)
        {
            this.ProcessLimitRows();
        }

        private void ProcessLimitRows()
        {
            //if (!this.chk_LimitRows.Checked || !int.TryParse(this.txt_Rows.Text, out this.maxrows) || !int.TryParse(this.txt_StartRow.Text, out this.startrow))
            if (!int.TryParse(this.txt_Rows.Text, out this.maxrows)
                || !int.TryParse(this.txt_StartRow.Text, out this.startrow)
                || !int.TryParse(this.txt_HeaderRow.Text,out this.headerrow))
            {
                this.maxrows = 10;
                this.startrow = this.useHeaderRow ? 2 : 1;
                this.headerrow = this.useHeaderRow ? 1 : -1;
            }

            if (this.useHeaderRow && this.startrow < this.headerrow + 1)
            {
                this.startrow = this.headerrow + 1;
            }
            else if (!this.useHeaderRow && this.startrow < 1)
            {
                this.startrow = 1;
            }

            this.RedirectToNewUrl();
        }

        private void List_Fields_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.requestedFieldNos = GetRequestedFieldsFromList();
            this.RedirectToNewUrl();
        }

        private void RedirectToNewUrl()
        {
            string datafilestr = "DataFileID=" + dataFile.ID;
            string getoptsstr = (this.requestedFieldNos.Count > 0 ? "&" + string.Join("&", this.requestedFieldNos.Select(f => string.Format("FieldNo={0}", f)).ToArray()) : "");
            string maxrowsstr = (this.maxrows > 0 ? "&MaxRows=" + maxrows : "");
            string startrowstr = (this.startrow > 0 ? "&StartRow=" + startrow : "");
            string headerrowstr = (this.headerrow > 0 ? "&HeaderRow=" + headerrow : "");
            string useheaderrowstr = (this.useHeaderRow ? "&UseHeaderRow=true" : "&UseHeaderRow=false");
            string delimStr = "&delim=" + HttpUtility.UrlEncode(this.delim.ToString());
            string redirectUrl = string.Format("{0}?{1}{2}{3}{4}{5}{6}{7}", this.Page.Request.Url.GetLeftPart(UriPartial.Path), datafilestr, getoptsstr, maxrowsstr, startrowstr, headerrowstr, useheaderrowstr, delimStr);
            this.Response.Redirect(redirectUrl);
        }

        private void CreateFieldSelectionList()
        {
            this.List_Fields.Items.Clear();

            //Add fields to list box
            this.List_Fields.Items.AddRange(this.delimitedFile.FieldNames.Select(kvp => new ListItem(kvp.Value, kvp.Key.ToString())).ToArray());

            //Check the fields that were requested
            foreach (int reqField in requestedFieldNos)
            {
                this.List_Fields.Items.FindByValue(reqField.ToString()).Selected = true;
            }

        }

        private void CreateTable()
        {
            //Remove existing rows
            this.ReportTable.Rows.Clear();

            HtmlTableRow trheadings = new HtmlTableRow();
            this.ReportTable.Rows.Add(trheadings);

            //Add a row number (blank for the first row)
            HtmlTableCell thcount = new HtmlTableCell("th");
            trheadings.Cells.Add(thcount);
            thcount.InnerText = "";

            //Add field names from dictionary as headings
            for (int i = 0; i < this.delimitedFile.FieldNames.Count; i++)
            {
                if (requestedFieldNos.Count() == 0 || requestedFieldNos.Contains(i))
                {
                    HtmlTableCell td = new HtmlTableCell("th");
                    trheadings.Cells.Add(td);
                    td.InnerText = this.delimitedFile.FieldNames[i];
                }
            }

            //Create the rest of the table

            Dictionary<int, string> fieldValues;
            while ((fieldValues = this.delimitedFile.GetNextRowValues()) != null)
            {
                //If maxrows <= 0 then it is not valid so we ignore it.
                if ((maxrows > 0) && (this.delimitedFile.CurrentLineNoRel > maxrows))
                {
                    //Break if we're done reading
                    break;
                }

                HtmlTableRow tr = new HtmlTableRow();

                //Add a row number
                HtmlTableCell tdcount = new HtmlTableCell();
                tr.Cells.Add(tdcount);
                tdcount.InnerText = this.delimitedFile.CurrentLineNoAbs.ToString();

                foreach (int fieldNo in this.delimitedFile.FieldNames.Keys)
                {

                    //If there are no specifically requested fields print the data, otherwise check the field was requested
                    if (requestedFieldNos.Count() == 0 || requestedFieldNos.Contains(fieldNo)) //requestedFieldNos is zero based, so is FieldNames.Keys
                    {
                        //Print the data
                        HtmlTableCell td = new HtmlTableCell();
                        tr.Cells.Add(td);
                        if (fieldValues.Count == this.delimitedFile.FieldCount && fieldValues.ContainsKey(fieldNo))
                        {
                            td.InnerText = fieldValues[fieldNo];
                        }
                        else
                        {
                            td.InnerText = "Line skipped";
                        }
                    }
                }

                this.ReportTable.Controls.Add(tr);
            }
        }

        private List<int> GetRequestedFields(string[] requestedFieldNos, string[] requestedFieldNames)
        {
            List<int> newFieldList = new List<int>();

            foreach(string reqField in requestedFieldNos)
            {
                foreach (KeyValuePair<int, string> fieldKVP in this.delimitedFile.FieldNames)
                {
                    if (fieldKVP.Key.Equals(Convert.ToInt32(reqField)) && !newFieldList.Contains(fieldKVP.Key))
                    {
                        newFieldList.Add(fieldKVP.Key);
                        break;
                    }
                }
            }

            foreach (string reqField in requestedFieldNames)
            {
                foreach (KeyValuePair<int, string> fieldKVP in this.delimitedFile.FieldNames)
                {
                    if (fieldKVP.Value.Equals(reqField, StringComparison.OrdinalIgnoreCase) && !newFieldList.Contains(fieldKVP.Key))
                    {
                        newFieldList.Add(fieldKVP.Key);
                        break;
                    }
                }
            }
            return newFieldList;
        }

        private List<int> GetRequestedFieldsFromList()
        {
            List<int> newFieldList = new List<int>();

            foreach (ListItem listitem in this.List_Fields.Items)
            {
                if (listitem.Selected)
                {
                    newFieldList.Add(int.Parse(listitem.Value));
                }
            }

            return newFieldList;
        }

        /*
                private void CreateTable_old()
                {
                    //Remove existing rows
                    this.ReportTable.Rows.Clear();

                    HtmlTableRow trheadings = new HtmlTableRow();
                    this.ReportTable.Rows.Add(trheadings);

                    //Add a row number (blank for the first row)
                    HtmlTableCell thcount = new HtmlTableCell("th");
                    trheadings.Cells.Add(thcount);
                    thcount.InnerText = "";

                    //Add field names from dictionary as headings
                    for (int i = 0; i < fieldDict.Count; i++)
                    {
                        if (requestedFieldNos.Count() == 0 || requestedFieldNos.Contains(i))
                        {
                            HtmlTableCell td = new HtmlTableCell("th");
                            trheadings.Cells.Add(td);
                            td.InnerText = fieldDict[i];
                        }
                    }

                    //Create the rest of the table

                    using (StreamReader sr = new StreamReader(dataFile.GetFullPath()))
                    {
                        //Our current line contents
                        string linecontents;

                        //Our current field number
                        int fieldno = 1;

                        //Count the line number we're on, but if we are not counting the first row as a header row, we need to count differently
                        int lineno = 1;

                        while ((linecontents = sr.ReadLine()) != null)
                        {
                            //If maxrows <= 0 then it is not valid so we ignore it.
                            if ((maxrows > 0) && (lineno >= (maxrows + startrow)))
                            {
                                //Break if we're done reading
                                break;
                            }

                            //Skip all rows up to startrow
                            if (lineno < startrow)
                            {
                                lineno++;
                                continue;
                            }

                            HtmlTableRow tr = new HtmlTableRow();

                            //Add a row number
                            HtmlTableCell tdcount = new HtmlTableCell();
                            tr.Cells.Add(tdcount);
                            int countLabel = lineno;
                            tdcount.InnerText = countLabel.ToString();

                            fieldno = 1;

                            foreach (string word in linecontents.Split(this.delim))
                            {
                                //If there are no specifically requested fields print the data, otherwise check the field was requested
                                if (requestedFieldNos.Count() == 0 || requestedFieldNos.Contains(fieldno - 1)) //requestedFieldNos is zero based
                                {
                                    //Print the data
                                    {
                                        HtmlTableCell td = new HtmlTableCell();
                                        tr.Cells.Add(td);
                                        td.InnerText = word;
                                    }
                                }
                                fieldno++;
                            }
                            this.ReportTable.Controls.Add(tr);
                            lineno++;
                        }
                    }
                }
        */

        /*
                private Dictionary<int, string> LoadFields_old()
                {
                    using (StreamReader sr = new StreamReader(dataFile.GetFullPath()))
                    {
                        Dictionary<int, string> fieldDict = new Dictionary<int, string>();
                        string line = null;
                        if ((line = sr.ReadLine()) != null)
                        {
                            int fieldno = 0;
                            this.linecount = 1;
                            foreach (string field in line.Split(this.delim))
                            {
                                string fieldname = this.firstRowIsHeader ? field : "Column " + (fieldno + 1);
                                fieldDict.Add(fieldno, fieldname);
                                fieldno++;
                            }
                            while ((line = sr.ReadLine()) != null)
                            {
                                linecount++;
                            }
                        }
                        return fieldDict;
                    }
                }
        */
    
    }
}
